use std::borrow::BorrowMut;
use std::io::{BufRead, BufReader, Write};
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::time::Duration;

fn main() {
    match std::env::args().skip(1).next().as_deref() {
        Some("send") => send(),
        Some("recv") => recv(),
        _ => panic!("命令错误"),
    }
}

/// 建立连接并发送 hello world
fn connect<A: ToSocketAddrs>(addr: A) -> std::io::Result<TcpStream> {
    let mut socket = TcpStream::connect(addr)?;
    let _ = socket.write_all(b"hello world\n");
    Ok(socket)
}

fn send() {
    let s1 = connect("127.0.0.1:80").unwrap();
    let s2 = connect("127.0.0.1:80").unwrap();

    std::thread::sleep(Duration::from_secs(10)); // 延迟10秒后释放连接

    drop(s1);
    drop(s2);
}

// 创建工人
fn create_workers(num: u32) -> mpsc::SyncSender<Box<dyn FnOnce() + Send>> {
    let (tx, rx) = mpsc::sync_channel::<Box<dyn FnOnce() + Send>>(8);
    let rx = Arc::new(Mutex::new(rx));

    (0..num).into_iter().for_each(|_| {
        let rx = rx.clone();
        std::thread::spawn(move || {
            while let Ok(task) = rx.lock().map(|r| r.recv()).unwrap() {
                task();
            }
        });
    });

    tx
}

fn recv() {
    let workers = create_workers(10);

    TcpListener::bind("0.0.0.0:80") // 绑定端口，监听连接
        .unwrap() // 如果绑定端口出现错误，则让程序 panic
        .incoming() // 接收端口上请求的连接
        .filter_map(|socket| socket.ok()) // 如果获取的连接发生错误，则过滤
        .map(BufReader::new) // 为连接添加缓冲区，这样可以读取一行文本
        .enumerate() // 为连接添加编号
        .for_each(|(num, mut socket)| {
            let task = move || {
                // 接收请求
                for line in socket.borrow_mut().lines() {
                    if let Ok(line) = line {
                        if line.is_empty() {
                            break;
                        }
                        println!("{num}: {line}");
                    }
                }
                // 返回响应
                socket
                    .get_mut()
                    .write_all(b"<html><body>hello world</body></html>")
                    .unwrap();
            };
            // 这里我们把任务发送到任务大厅
            let _ = workers.try_send(Box::new(task));
        })
}
